package com.ash.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class ArithmeticLoggerWrapper {

	/**
	 * 定义一个方法，用于声明切入点表达式，一般的，该方法中不再需要添入其他的代码
	 */
	@Pointcut("execution(* com.ash.aop.Arithmetic.*(..))")
	public void declarePointCutExpression() {

	}

	// 声明该方法是一个前置通知：在目标方法开始之前执行
	// 如果目标方法跟我这个before的声明的方法的一致的时候，aop框架会自动的把方法所在的类生成一个代理对象
	/**
	 * 在com.juggernaut.aop.IArithmetic接口的每一个实现类的每一个方法开始之前执行一段代码
	 * 
	 */
	@Before("execution(* com.ash.aop.IArithmetic.*(..))")
	public void beforeMethod(JoinPoint joinPoint) {
		String methoName = joinPoint.getSignature().getName();
		System.out.println(methoName + " method begins " + Arrays.asList(joinPoint.getArgs()));
	}

	// 后置通知：在目标方法执行后（无论是否放生异常），执行的通知
	// 在后置通知中还不能访问目标方法的执行的结果
	@After("declarePointCutExpression()")
	public void afterMethod(JoinPoint joinPoint) {
		String methoName = joinPoint.getSignature().getName();
		System.out.println(methoName + " method ends ");
	}

	/**
	 * 
	 * 在方法的正常结束后执行的代码
	 * 
	 */
	// 返回通知
	@AfterReturning(value = "declarePointCutExpression()", returning = "result")
	public void afterReturning(JoinPoint joinPoint, Object result) {
		String methoName = joinPoint.getSignature().getName();
		System.out.println(methoName + " method ends with " + result);
	}

	@AfterThrowing(value = "execution(* com.ash.aop.Arithmetic.*(..))", throwing = "e")
	public void afterThrowing(JoinPoint joinPoint, Exception e) {
		String methoName = joinPoint.getSignature().getName();
		System.out.println("an exception" + e + " has been throwing in  " + methoName);
	}

	/**
	 * 环绕通知需要携带 ProceedingJoinPoint 类型的参数
	 * 环绕通知类似于动态代理的全过程：ProceedingJoinPoint 类型的参数可以决定是否执行目标方法。
	 * 且环绕通知必须有返回值，返回值即目标方法的的返回值
	 * 
	 */
	@Around("execution(* com.ash.aop.Arithmetic.*(..))")
	public Object aroundMethod(ProceedingJoinPoint pjd) {
		Object result = null;
		String methodName = pjd.getSignature().getName();
		try {
			// 前置通知
			System.out.println("around begin");
			// 执行目标方法
			result = pjd.proceed();
			// 返回通知
			System.out.println("around end with " + result);

		} catch (Throwable e) {
			e.printStackTrace();
			System.out.println("around exception");
		}

		System.out.println("around ends");

		return result;
	}
}
